package com.xiyuan.smartutils.tools;

import com.xiyuan.smartutils.Arrays;
import com.xiyuan.smartutils.filters.Filter;

import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author xiyuan-lgz 2024-09-29 @version v1.0.0 新建与整理
 */
@SuppressWarnings("all")
public class Array<T> implements Serializable, Iterable<T> {
    
    
    public static Array<Boolean> from(boolean... values) {
        return new Array<>(values == null || values.length == 0 ? new Boolean[0] : Arrays.convert(values));
    }
    
    public static Array<Byte> from(byte... values) {
        return new Array<>(values == null || values.length == 0 ? new Byte[0] : Arrays.convert(values));
    }
    
    public static Array<Short> from(short... values) {
        return new Array<>(values == null || values.length == 0 ? new Short[0] : Arrays.convert(values));
    }
    
    public static Array<Character> from(char... values) {
        return new Array<>(values == null || values.length == 0 ? new Character[0] : Arrays.convert(values));
    }
    
    public static Array<Integer> from(int... values) {
        return new Array<>(values == null || values.length == 0 ? new Integer[0] : Arrays.convert(values));
    }
    
    public static Array<Long> from(long... values) {
        return new Array<>(values == null || values.length == 0 ? new Long[0] : Arrays.convert(values));
    }
    
    public static Array<Float> from(float... values) {
        return new Array<>(values == null || values.length == 0 ? new Float[0] : Arrays.convert(values));
    }
    
    public static Array<Double> from(double... values) {
        return new Array<>(values == null || values.length == 0 ? new Double[0] : Arrays.convert(values));
    }
    
    private LinkedList<T> values = new LinkedList<T>();
    private final ReentrantLock lock = new ReentrantLock();
    public Array() {
    }
    
    public Array(T... values) {
        if (values != null) {
            this.values = new LinkedList<T>(Arrays.asList(values));
        }
    }
    
    public Array(Collection<T> values) {
        if (values != null) {
            this.values = new LinkedList<T>(values);
            
        }
    }
    
    public int length() {
        return values.size();
    }
    
    private boolean isEmpty() {
        return values.isEmpty();
    }
    
    public T get(int index) {
        return index >= 0 && index < values.size() ? values.get(index) : null;
    }
    
    public T set(int index, T value) {
        return values.set(index, value);
    }
    
    /**
     * @param value 数据值
     * @return 返回 数据长度
     */
    public int push(T value, T... vals) {
        lock.lock();
        try {
            values.addLast(value);
            if (vals != null) {
                for (T val : vals) values.addLast(val);
            }
            return values.size();
        }
        finally {
            lock.unlock();
        }
    }
    
    /**
     * 返回当前对象
     *
     * @param value 填充值
     * @return 当前对象
     */
    public Array<T> fill(T value) {
        return fill(value, 0, length());
    }
    
    /**
     * @param value 填充值
     * @param start 开始索引
     * @return 当前对象
     */
    public Array<T> fill(T value, int start) {
        return fill(value, start, length());
    }
    
    /**
     * @param value 填充值
     * @param start 开始索引
     * @param end   结束索引 不包括当前 结束索引
     * @return 当前对象
     */
    public Array<T> fill(T value, int start, int end) {
        
        final ReentrantLock lock = this.lock;
        lock.lock(); // Never contended, but necessary for visibility
        try {
            start = Math.min(Math.max(start, 0), this.length());
            if (this.length() > 0) {
                for (int i = start; i < end; i++) {
                    this.values.set(i, value);
                }
            }
        }
        finally {
            lock.unlock();
        }
        
        return this;
    }
    
    /**
     * 弹出最后一条数据（删除最后 条数据）
     *
     * @return 返回 最后一条数据
     */
    public T pop() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return this.values.pollLast();
        }
        finally {
            lock.unlock();
        }
    }
    
    /**
     * 弹出第一条条数据（删除第一条数据）
     *
     * @return 第一条数据
     */
    public T shift() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            
            return this.values.pollFirst();
        }
        finally {
            lock.unlock();
        }
    }
    
    
    public String join() {return join(",");}
    
    /**
     * Adds all the elements of an array into a string, separated by the specified separator string.
     *
     * @param separator A string used to separate one element of the array from the next in the resulting string. If omitted, the array elements are separated with a comma.
     */
    public String join(String separator) {
        if (separator == null) {
            separator = ",";
        }
        return this.values.stream().map(String::valueOf).collect(Collectors.joining(separator));
    }
    
    
    /**
     * 分割数数据
     *
     * @return new Array
     */
    public Array<T> slice() {
        return this;
    }
    
    /**
     * 分割数数据
     *
     * @param start 开始索引
     * @param end   结束索引
     * @return
     */
    public Array<T> slice(int start, int end) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (start < 0) {
                // 从后面截取
                start = Math.max(this.length() + start, 0);
            }
            end = Math.max(Math.min(this.length(), end), 0);
            
            int count = end - start;
            if (this.length() == 0 || count <= 0) {
                return new Array<>();
            }
            
            
            return new Array<>(this.values.subList(start, end));
        }
        finally {
            lock.unlock();
        }
    }
    
    /**
     * 分割数数据
     *
     * @param start 开始索引
     */
    public Array<T> slice(int start) {
        return slice(start, length());
    }
    
    @SuppressWarnings("unchecked")
    public Array<T> splice(int start) {
        return splice(start, this.length());
    }
    
    public Array<T> splice(int start, int deleteCount, T... values) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        
        try {
            
            
            if (start < 0) {
                // 从后面截取
                start = Math.max(this.length() + start, 0);
            }
            int end = start;
            if (deleteCount < 0) {
                deleteCount = 0;
            }
            if (deleteCount > 0) {
                end = start + deleteCount;
            }
            end = Math.max(Math.min(this.length(), end), 0);
            
            int insertCount = values == null ? 0 : values.length;
            deleteCount = end - start;
            
            
            LinkedList<T> list = this.values;
            List<T> deleted = new ArrayList<>(0);
            // 没有改动
            if (deleteCount == 0 && insertCount == 0) {
                return new Array<T>();
            }
            
            // 先删除
            for (int i = deleteCount; i > 0; i--) {
                deleted.add(list.remove(start));
            }
            
            // 再追加
            for (int i = insertCount - 1; i >= 0; i--) {
                list.add(start, values[i]);
            }
            
            return new Array<>(deleted);
        }
        finally {
            lock.unlock();
        }
    }
    
    public <R> Array<R> map(Function<? super T, ? extends R> mapper) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return new Array<>(this.values.stream().map(mapper).collect(Collectors.toList()));
        }
        finally {
            lock.unlock();
        }
    }
    
    public <R> Array<R> map(ArrayMap2<? super T, R> map) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            
            AtomicInteger seq = new AtomicInteger(0);
            return new Array<R>(this.values.stream()
                                        .map((r) -> map.map(r, seq.getAndIncrement()))
                                        .collect(Collectors.toList()));
        }
        finally {
            lock.unlock();
        }
        
    }
    
    public <R> Array<R> map(ArrayMap3<? super T, R, Array<T>> map) {
        AtomicInteger seq = new AtomicInteger(0);
        return new Array<R>(this.values.stream()
                                    .map((r) -> map.map(r, seq.getAndIncrement(), this))
                                    .collect(Collectors.toList()));
    }
    
    public Array<T> filter(Filter.Filter1<T> filter) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return new Array<>(this.values.stream()
                                       .filter(filter::filter)
                                       .collect(Collectors.toList()));
        }
        finally {
            lock.unlock();
        }
    }
    
    
    public Array<T> filter(Filter.Filter2<T> filter) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            AtomicInteger idx = new AtomicInteger(0);
            return new Array<>(this.values.stream()
                                       .filter(r -> filter.filter(r, idx.getAndIncrement()))
                                       .collect(Collectors.toList()));
        }
        finally {
            lock.unlock();
        }
    }
    
    public Array<T> filter(Filter.Filter3<T, Array<T>> filter) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            AtomicInteger idx = new AtomicInteger(0);
            return new Array<>(this.values.stream()
                                       .filter(r -> filter.filter(r, idx.getAndIncrement(), this))
                                       .collect(Collectors.toList()));
        }
        finally {
            lock.unlock();
        }
    }
    
    
    @Override
    public Iterator<T> iterator() {
        return this.values.iterator();
    }
    
    @Override
    public void forEach(Consumer<? super T> action) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            
            this.values.forEach(action);
        }
        finally {
            lock.unlock();
        }
    }
    
    public void forEach(ArrayEach2<? super T> forEach) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            AtomicInteger seq = new AtomicInteger(0);
            this.values.forEach((r) -> forEach.each(r, seq.getAndIncrement()));
        }
        finally {
            lock.unlock();
        }
    }
    
    public void forEach(ArrayEach3<T, Array<T>> forEach) {
        AtomicInteger seq = new AtomicInteger(0);
        this.values.forEach((r) -> forEach.each(r, seq.getAndIncrement(), this));
    }
    
    public Array<T> accept(Consumer<? super Array<T>> action) {
        action.accept(this);
        return this;
    }
    
    
    @FunctionalInterface
    public static interface ArrayMap2<T, R> {
        R map(T value, int index);
    }
    
    @FunctionalInterface
    public static interface ArrayMap3<T, R, C> {
        R map(T value, int index, C c);
    }
    
    @FunctionalInterface
    public static interface ArrayEach2<T> {
        void each(T value, int index);
    }
    
    @FunctionalInterface
    public static interface ArrayEach3<T, C> {
        void each(T value, int index, C c);
    }
    
    @FunctionalInterface
    public static interface Reducer<T, C> {
        public T reduce(T prevValue, T currValue, int currIdx, C _this);
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        boolean first = false;
        for (T t : values) {
            if (first) sb.append(", ");
            sb.append(t);
            first = true;
        }
        sb.append("]");
        return sb.toString();
    }
    
    public T[] toArray() {
        Object value = this.values.stream().filter(Objects::nonNull).findFirst().orElse((T) new Object());
        return this.values.toArray((T[]) java.lang.reflect.Array.newInstance(value.getClass(), 0));
    }
    
    public <R> R[] toArray(R[] array) {
        return this.values.toArray(array);
    }
    
    public <R> R[] toArray(Class<R> rClass) {
        return this.values.toArray((R[]) java.lang.reflect.Array.newInstance(rClass, 0));
    }
    
}
